/**
 * @file pdistr_base.h
 *
 * The basics for implementing probability distributions
 * 
 * @author Dahua Lin
 */

#ifndef SLIPP_PDISTR_BASE_H_
#define SLIPP_PDISTR_BASE_H_

#include <slipp/base/basic_defs.h>
#include <slipp/base/lang_utils.h>
#include <slipp/base/math_base.h>
#include <slipp/base/arrays.h>

#include <slipp/prob_base/rstream.h>

namespace sli
{
	template<class T> struct is_pdistr { static const bool value = false; };

	template<class Distr> struct pdistr_traits;

	template<class Distr, bool IsSampleable, bool IsPdfEvaluable>
	struct pdistr_traits_base
	{
		typedef typename Distr::scalar_type scalar_type;
		typedef typename Distr::sample_type sample_type;
		typedef typename Distr::sample_collection_type sample_collection_type;

		typedef typename Distr::param_type param_type;

		static const bool is_sampleable = IsSampleable;
		static const bool is_pdf_evaluable = IsPdfEvaluable;
	};


	template<class Distr, class RStream> struct sampler_of;

	template<class Distr> struct pdf_evaluator_of;


	// facilities for pdf/log-pdf evaluation

	namespace _detail
	{
		template<typename PDFEvaluator>
		struct pdf_eval_functor
		{
			typedef typename PDFEvaluator::sample_type sample_type;
			typedef double result_type;

			const PDFEvaluator& m_evaluator;
			pdf_eval_functor(const PDFEvaluator& e) : m_evaluator(e) { }

			result_type operator() (const sample_type& x) const
			{
				return m_evaluator.pdf(x);
			}
		};


		template<typename PDFEvaluator>
		struct logpdf_eval_functor
		{
			typedef typename PDFEvaluator::sample_type sample_type;
			typedef double result_type;

			const PDFEvaluator& m_evaluator;
			logpdf_eval_functor(const PDFEvaluator& e) : m_evaluator(e) { }

			result_type operator() (const sample_type& x) const
			{
				return m_evaluator.logpdf(x);
			}
		};

		template<typename PDFEvaluator>
		inline pdf_eval_functor<PDFEvaluator> make_pdf_eval_functor(const PDFEvaluator& e)
		{
			return pdf_eval_functor<PDFEvaluator>(e);
		}

		template<typename PDFEvaluator>
		inline logpdf_eval_functor<PDFEvaluator> make_logpdf_eval_functor(const PDFEvaluator& e)
		{
			return logpdf_eval_functor<PDFEvaluator>(e);
		}
	}

}


#define SLIPP_DEFINE_SCALAR_DISTR_TYPES(T) \
	typedef T scalar_type; \
	typedef scalar_type sample_type; \
	typedef Eigen::Array<scalar_type, 1, Eigen::Dynamic> sample_collection_type;

#define SLIPP_DEFINE_VECTOR_DISTR_TYPES(T) \
	typedef T scalar_type; \
	typedef Eigen::Matrix<scalar_type, Eigen::Dynamic, 1> sample_type; \
	typedef Eigen::Matrix<scalar_type, Eigen::Dynamic, Eigen::Dynamic> sample_collection_type;


#define SLIPP_DEFINE_BASIC_DISTR_TRAITS(Distr, SamplerT) \
	template<> struct is_pdistr<Distr> { \
		static const bool value = true; }; \
	template<> \
	struct pdistr_traits<Distr> : public pdistr_traits_base<Distr, true, true> { }; \
	template<class RStream> \
	struct sampler_of<Distr, RStream> { \
		typedef SamplerT<RStream> type; }; \
	template<> \
	struct pdf_evaluator_of<Distr> { \
		typedef Distr::pdf_evaluator_type type; };

#endif 
